﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows.Input;
using Foundation;
using Foundation.Commands;
using Foundation.ExtensionMethods;
using Foundation.FileAssotiations;
using ICSharpCode.AvalonEdit.Highlighting;
using Microsoft.Win32;
using Poet.Properties;

namespace Poet.ViewModels
{
    public class AssociationViewModel : ViewModel
    {
        public static readonly string[] InlinedExtensions = new[] {".txt", ".cfg", ".conf", ".xml", ".json"};

        public static readonly ObservableCollection<FileAssociation> DefaultItems = new ObservableCollection<FileAssociation>();

        public static readonly string SupportedExtensionsConfigurationFile = Settings.Default.SupportedExtensionsFile;

        public static string SupportedExtensionsConfigurationFilePath;

        static AssociationViewModel()
        {
            SupportedExtensionsConfigurationFilePath =
                string.Format(@"{0}\{1}",
                              AppDomain.CurrentDomain.BaseDirectory,
                              SupportedExtensionsConfigurationFile);
            DefaultItems.AddRange(InlinedExtensions.Select(e => new FileAssociation(e)));
            DefaultItems.AddRange(GetAssotiationsFromHighlightingManager().Where(a => DefaultItems.All(b => b.ExtensionInfo.Extension != a.ExtensionInfo.Extension)));
            DefaultItems.AddRange(GetTextAssotiationsFromRegistry().Where(a => DefaultItems.All(b => b.ExtensionInfo.Extension != a.ExtensionInfo.Extension)));
        }

        public static bool IsAssotiationConfigurationFileExists
        {
            get { return File.Exists(SupportedExtensionsConfigurationFilePath); }
        }

        public AssociationViewModel()
        {
            Add = new RoutedUICommand();
            Remove = new RoutedUICommand();

            SelectedItems = new ObservableCollection<FileAssociation>();
            Items = new ObservableCollection<FileAssociation>(DefaultItems);
            Items.AddRange(GetAssotiationsFromConfigurationFile().Where(a => Items.All(b => b.ExtensionInfo.Extension != a.ExtensionInfo.Extension))); 
            Items = new ObservableCollection<FileAssociation>(Items.OrderBy(x => x.ExtensionInfo.Extension));
            InternalItems = new List<FileAssociation>(Items);
            
            SetAssotiationsToConfigurationFile(Items);

            PropertyChanged += OnPropertyChanged;
            OnClosing += (o, e) =>
                    {
                        Items = new ObservableCollection<FileAssociation>(
                            Items.OrderBy(x => x.ExtensionInfo.Extension));
                        SetAssotiationsToConfigurationFile(Items);
                    };
            CommandBindings.Add(new CommandBinding(SelectionCommands.CheckAll, ExecutedSelectionCommand, CanExecuteSelectionCommand));
            CommandBindings.Add(new CommandBinding(SelectionCommands.UncheckAll, ExecutedSelectionCommand, CanExecuteSelectionCommand));
            CommandBindings.Add(new CommandBinding(SelectionCommands.CheckSelected, ExecutedSelectionCommand, CanExecuteSelectionCommand));
            CommandBindings.Add(new CommandBinding(SelectionCommands.UncheckSelected, ExecutedSelectionCommand, CanExecuteSelectionCommand));
            CommandBindings.Add(new CommandBinding(Add, ExecutedAdd, CanExecuteAdd));
            CommandBindings.Add(new CommandBinding(Remove, ExecutedRemove, CanExecuteRemove));
        }

        public ICommand Add { get; set; }

        public ICommand Remove { get; set; }

        public ObservableCollection<FileAssociation> SelectedItems { get; set; }

        public ObservableCollection<FileAssociation> Items { get; set; }

        public List<FileAssociation> InternalItems { get; set; }

        public string Filter
        {
            get { return Get(() => Filter); }
            set { Set(() => Filter, value); }
        }

        public string ExtensionToAddOrRemove
        {
            get { return Get(() => ExtensionToAddOrRemove); }
            set { Set(() => ExtensionToAddOrRemove, value); }
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == GetPropertyName(() => Filter))
            {
                Items.Clear();
                Items.AddRange(string.IsNullOrEmpty(Filter)
                                   ? InternalItems
                                   : InternalItems.Where(
                                       a =>
                                       Filter.Split(new[] { ' ', ',' }).Where(p => !string.IsNullOrWhiteSpace(p)).
                                           Any(pattern => a.ExtensionInfo.Extension.Contains(pattern))));
            }
        }

        private void CanExecuteAdd(object sender, CanExecuteRoutedEventArgs args)
        {
            var extensionToAddOrRemove = ExtensionToAddOrRemove != null && ExtensionToAddOrRemove.StartsWith(".")
                                 ? ExtensionToAddOrRemove
                                 : string.Format(".{0}", ExtensionToAddOrRemove);
            args.CanExecute = extensionToAddOrRemove.Length > 1;
        }

        private void ExecutedAdd(object sender, ExecutedRoutedEventArgs args)
        {
            Items.Add(new FileAssociation(ExtensionToAddOrRemove));
        }

        private void CanExecuteRemove(object sender, CanExecuteRoutedEventArgs args)
        {
            var extensionToAddOrRemove = ExtensionToAddOrRemove != null && ExtensionToAddOrRemove.StartsWith(".")
                                             ? ExtensionToAddOrRemove
                                             : string.Format(".{0}", ExtensionToAddOrRemove);
            args.CanExecute =
                DefaultItems.FirstOrDefault(i => i.ExtensionInfo.Extension == extensionToAddOrRemove) == null &&
                Items.FirstOrDefault(i => i.ExtensionInfo.Extension == extensionToAddOrRemove) != null;
        }

        private void ExecutedRemove(object sender, ExecutedRoutedEventArgs args)
        {
            Items.Add(new FileAssociation(ExtensionToAddOrRemove));
        }

        private void CanExecuteSelectionCommand(object sender, CanExecuteRoutedEventArgs args)
        {
            if (args.Command == SelectionCommands.CheckSelected)
                args.CanExecute = SelectedItems.Any(a => !a.IsAssotiated);            
            if (args.Command == SelectionCommands.UncheckSelected)
                args.CanExecute = SelectedItems.Any(a => a.IsAssotiated);            
            if (args.Command == SelectionCommands.CheckAll)
                args.CanExecute = Items.Any(a => !a.IsAssotiated);            
            if (args.Command == SelectionCommands.UncheckAll)
                args.CanExecute = Items.Any(a => a.IsAssotiated);
        }

        private void ExecutedSelectionCommand(object sender, ExecutedRoutedEventArgs args)
        {
            if (args.Command == SelectionCommands.CheckSelected)
                SelectedItems.ForEach(i => i.IsAssotiated = true);
            if (args.Command == SelectionCommands.UncheckSelected)
                SelectedItems.ForEach(i => i.IsAssotiated = false);
            if (args.Command == SelectionCommands.CheckAll)
                Items.ForEach(i => i.IsAssotiated = true);
            if (args.Command == SelectionCommands.UncheckAll)
                Items.ForEach(i => i.IsAssotiated = false);
        }

        public static List<FileAssociation> GetAssotiationsFromHighlightingManager()
        {
            return HighlightingManager.Instance.GetDefaultExtensions().Select(e => new FileAssociation(e)).ToList();
        }

        public List<FileAssociation> GetAssotiationsFromConfigurationFile()
        {
            if (!File.Exists(SupportedExtensionsConfigurationFile))
                return new List<FileAssociation>();

            using (var reader = new StreamReader(SupportedExtensionsConfigurationFile))
            {
                var assotiations = new List<FileAssociation>();
                for (; !reader.EndOfStream; reader.ReadLine())
                {
                    var extension = reader.ReadLine();
                    var association = new FileAssociation(extension);
                    assotiations.Add(association);
                }
                return assotiations;
            }
        }     

        public static void SetAssotiationsToConfigurationFile(IEnumerable<FileAssociation> assotiations)
        {
            using (var writer = new StreamWriter(new FileStream(SupportedExtensionsConfigurationFilePath, FileMode.OpenOrCreate)))
            {
                writer.BaseStream.SetLength(0);
                foreach (var assotiation in assotiations)
                {
                    writer.WriteLine(assotiation.ExtensionInfo.Extension);
                }
            }
        }

        public static List<FileAssociation> GetTextAssotiationsFromRegistry()
        {
            return GetAllAssotiationsFromRegistry().Select(e => new FileAssociation(e)).Where(a => a.ExtensionInfo.PerceivedType == "text").ToList();
        }

        public static List<string> GetAllAssotiationsFromRegistry()
        {
            return Registry.ClassesRoot.GetSubKeyNames().Where(subKey => subKey.StartsWith(".")).ToList();
        }
    }
}
